using System;
using System.Collections;
using System.Data;
using System.Data.SQLite;

namespace WIT.DAQ.Core.Helper;

/// <summary>
/// SQLiteHelperParameterCache provides functions to leverage a static cache of procedure parameters, and the
/// ability to discover parameters for stored procedures at run-time.
/// </summary>
internal sealed class SQLiteHelperParameterCache
{
	private static Hashtable paramCache = Hashtable.Synchronized(new Hashtable());

	private SQLiteHelperParameterCache()
	{
	}

	/// <summary>
	/// resolve at run-time the appropriate set of SQLiteParameters for a stored procedure
	/// </summary>
	/// <param name="connectionString">a valid connection string for a SQLiteConnection</param>
	/// <param name="spName">the name of the stored prcedure</param>
	/// <param name="includeReturnValueParameter">weather or not to onclude ther return value parameter</param>
	/// <returns></returns>
	private static SQLiteParameter[] DiscoverSpParameterSet(string connectionString, string spName, bool includeReturnValueParameter)
	{
		using SQLiteConnection sQLiteConnection = new SQLiteConnection(connectionString);
		using SQLiteCommand sQLiteCommand = new SQLiteCommand(spName, sQLiteConnection);
		sQLiteConnection.Open();
		sQLiteCommand.CommandType = CommandType.StoredProcedure;
		if (!includeReturnValueParameter && ParameterDirection.ReturnValue == sQLiteCommand.Parameters[0].Direction)
		{
			sQLiteCommand.Parameters.RemoveAt(0);
		}
		SQLiteParameter[] array = new SQLiteParameter[sQLiteCommand.Parameters.Count];
		sQLiteCommand.Parameters.CopyTo(array, 0);
		return array;
	}

	private static SQLiteParameter[] CloneParameters(SQLiteParameter[] originalParameters)
	{
		SQLiteParameter[] array = new SQLiteParameter[originalParameters.Length];
		int i = 0;
		for (int num = originalParameters.Length; i < num; i++)
		{
			array[i] = (SQLiteParameter)((ICloneable)originalParameters[i]).Clone();
		}
		return array;
	}

	/// <summary>
	/// add parameter array to the cache
	/// </summary>
	/// <param name="connectionString">a valid connection string for an SQLiteConnection</param>
	/// <param name="commandText">the stored procedure name or T-SQLite command</param>
	/// <param name="commandParameters">an array of SQLiteParamters to be cached</param>
	internal static void CacheParameterSet(string connectionString, string commandText, params SQLiteParameter[] commandParameters)
	{
		string key = connectionString + ":" + commandText;
		paramCache[key] = commandParameters;
	}

	/// <summary>
	/// retrieve a parameter array from the cache
	/// </summary>
	/// <param name="connectionString">a valid connection string for a SQLiteConnection</param>
	/// <param name="commandText">the stored procedure name or T-SQLite command</param>
	/// <returns>an array of SQLiteParameters</returns>
	internal static SQLiteParameter[] GetCachedParameterSet(string connectionString, string commandText)
	{
		string key = connectionString + ":" + commandText;
		SQLiteParameter[] array = (SQLiteParameter[])paramCache[key];
		if (array == null)
		{
			return null;
		}
		return CloneParameters(array);
	}

	/// <summary>
	/// Retrieves the set of SQLiteParameters appropriate for the stored procedure
	/// </summary>
	/// <remarks>
	/// This method will query the database for this information, and then store it in a cache for future requests.
	/// </remarks>
	/// <param name="connectionString">a valid connection string for a SQLiteConnection</param>
	/// <param name="spName">the name of the stored prcedure</param>
	/// <returns>an array of SQLiteParameters</returns>
	internal static SQLiteParameter[] GetSpParameterSet(string connectionString, string spName)
	{
		return GetSpParameterSet(connectionString, spName, includeReturnValueParameter: true);
	}

	/// <summary>
	/// Retrieves the set of SQLiteParameters appropriate for the stored procedure
	/// </summary>
	/// <remarks>
	/// This method will query the database for this information, and then store it in a cache for future requests.
	/// </remarks>
	/// <param name="connectionString">a valid connection string for an SQLiteConnection</param>
	/// <param name="spName">the name of the stored procedure</param>
	/// <param name="includeReturnValueParameter">a bool value indicating weather the return value parameter should be included in the results</param>
	/// <returns>an array of SQLiteParameters</returns>
	internal static SQLiteParameter[] GetSpParameterSet(string connectionString, string spName, bool includeReturnValueParameter)
	{
		string key = connectionString + ":" + spName + (includeReturnValueParameter ? ":include ReturnValue Parameter" : "");
		SQLiteParameter[] array = (SQLiteParameter[])paramCache[key];
		if (array == null)
		{
			object obj = (paramCache[key] = DiscoverSpParameterSet(connectionString, spName, includeReturnValueParameter));
			array = (SQLiteParameter[])obj;
		}
		return CloneParameters(array);
	}
}
